﻿#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//class Date
//{
//public:
//	void Init(int year, int month, int day)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
//	void Print()//可以在汇编中看到地址
//	{
//		cout << _year << "/" << _month << "/" << _day << endl;
//	}
//
////private:
//	// 这⾥只是声明，没有开空间
//	int _year;   // 声明
//	int _month;
//	int _day;
//};
//
//int main()
//{
//	Date d1; // 类实例化对象
//	Date d2;
//	
//	d1.Init(2024, 12, 1);
//	d2.Init(2025, 11, 2);
//	
//	// 这两个_year需要不同空间存储独立的值
//	d1._year++;
//	d2._year++;
//	
//	// Print函数指针是一样的，不需要独立的空间
//	d1.Print();
//	d2.Print();
//	
//	cout << sizeof(Date) << endl;//12
//	cout << sizeof(d1) << endl;
//
//	return 0;
//}

//--------------------------------------------------------------------------
////实例化出的对象的所占空间大小
//class A//内存的大小只看成员变量，不看成员函数，因为你调用的函数是这个类通用的，没必要在每个对象里放函数的地址，即64位下8字节的空间
//{
//public:
//	void Print()
//	{
//		cout << _ch << endl;
//	}
//private:
//	int _i;
//	char _ch;
//	//int _i;
//};
//
//class B//这种是一个字节，纯粹是为了占位标识对象存在。
//{
//public:
//	void Print()
//	{
//		//...
//	}
//};
//
//class C//这种是一个字节，纯粹是为了占位标识对象存在。
//{};
//
//int main()
//{
//	cout << sizeof(A) << endl;//整体大小要与最大对齐数对齐
//	cout << sizeof(B) << endl;
//	cout << sizeof(C) << endl;
//
//	// 没有成员变量的类对象，分配1byte纯粹为了占位，表示对象存在
//	B b1;
//	B b2;
//	cout << &b1 << endl;
//	cout << &b2 << endl;
//
//	return 0;
//}
//--------------------------------------------------------------------
// this指针
//class Date
//{
//public:
//	void Init(int year, int month, int day)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//
//	void Print()
//	{
//		cout << this << endl;
//		//this = nullptr; this本身不能修改
//		cout << this->_year << "/" << this->_month << "/" << _day << endl;
//	}
//
//	//void Print(Date* const this)//说明this的指向不能变，即this指针变量里存的地址不能变。但是可以修改其指向的东西。这个是隐式的，编译器会帮我们做
//	//{
//	//	cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
//	//}
//
//private:
//	int _year;   // 声明
//	int _month;
//	int _day;
//};
//
//int main()
//{
//	Date d1;
//	Date d2;
//
//	cout << &d1 << endl;
//	cout << &d2 << endl << endl;
//
//
//	d1.Init(2024, 12, 1);
//	d2.Init(2025, 11, 2);
//
//	d1.Print();
//	d2.Print();
//
//	//d1.Print(&d1);//编译器做的事
//	//d2.Print(&d2);
//
//	return 0;
//}

//class A
//{
//public:
//	void Print()
//	{
//		this->_a = 100;
//		cout << this << endl;
//		cout << "A::Print()" << endl;
//		cout << _a << endl;
//	}
//private:
//	int _a;
//};
//
//int main()
//{
//	A aa;
//	A* p = &aa;
//	cout << &aa << endl << endl;
//	p->Print(); // call Print地址
//
//	aa.Print();
//	return 0;
//}

//class A
//{
//public:
//	void Print()
//	{
//		cout << this << endl;
//		//this->_a = 100;//这里对空指针解引用了
//		cout << "A::Print()" << endl;
//		//cout << _a << endl;//这里也是
//	}
//private:
//	int _a;
//};
//
//int main()
//{
//	A* p = nullptr;
//	//A* p;//这样会报错，因为你现在只是开辟了指针变量所需的8字节空间，但是你没有定义这8字节空间里存放的哪里的地址，会出现报错（里面存的值是不确定的（可能是随机垃圾值或非法内存地址））
//	(*p).Print(/*传参的就是对象的地址，即p（编译器帮我们干）*/); // call Print地址,这里并没有解引用，因为C++看到是直接理解为你要这个函数，而不是先找到对象再找到函数，因为函数本身就不在对象里存着，这是在编译的时候（如果没有声明与定义分开）就知道函数地址了
//	p->Print();
//
//	return 0;
//}

//--------------------------------------
//C和C++实现栈有一些区别，尤其是在传参方面，C++的栈是定义一个类，所以调用都是 对象.函数名();
//而C要传栈结构体指针，和其他参数

//------------------------------------------------------------------------
//C++类和对象中
//默认成员函数
//我们要从两个方面去学习：
//• 第⼀：我们不写时，编译器默认⽣成的函数行为是什么，是否满⾜我们的需求。
//• 第⼆：编译器默认⽣成的函数不满⾜我们的需求，我们需要自己实现，那么如何自己实现？

//第一个构造函数
//构造函数的主要任务并不是开空间创建对象(我们常使用的局部对象是栈帧创建时，空间就开好了)，⽽是对象实例化时初始化对象。
//构造函数的本质是要替代我们以前Stack和Date类中写的Init函数的功能，构造函数自动调用的特点就完美的替代的了Init。

//class Date
//{
//public:
//	////1.⽆参构造函数
//	Date()//1.函数名与类名相同。2.⽆返回值。3.对象实例化时系统会⾃动调⽤对应的构造函数。
//	{
//		_year = 1;
//		_month = 1;
//		_day = 1;
//	}
//	////2.带参的构造函数
//	Date(int year, int month, int day)//4.构造函数可以重载。
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//	}
//	//3.全缺省构造函数
//	//Date(int year=1, int month=1, int day=1)
//	//{
//	//	_year = year;
//	//	_month = month;
//	//	_day = day;
//	//}
//	void Print()
//	{
//		cout <<_year << "/" << _month << "/" << _day << endl;
//	}
//private:
//	// 内置类型
//	int _year;//可以提前初始化int _year=0;
//	int _month;
//	int _day;
//};
//
//int main()
//{
//	// 对象实例化一定会调用对应的构造，保证了对象实例化出来一定被初始化了
//
//	//当我注释掉1 2 3构造函数时就是编译器自动生成的默认构造函数给我初始化  //1 3是默认构造函数
//	Date n1;//当我注释掉1 2 3构造函数时自动调用的是默认构造函数。
//	//那个带参的也是构造函数，但是不是默认构造函数，默认构造函数是不带参的、全缺省的和我们不写构造时编译器默认⽣成的构造函数
//	//默认构造函数只能有三个其中一个，因为不带参和全缺省的函数在调用时会出现歧义，而当你写了这两个其中一个，即有显式定义构造函数，即不会自动生成⼀个⽆参的默认构造函数
//	//当你这两个都没写，编译器会自动生成⼀个⽆参的默认构造函数，对内置类型成员变量（是语⾔提供的原⽣数据类型，如：int / char / double / 指针等，）的初始化没有要求，也就是说是是否初始化是不确定的，看编译器。
//	// 对于⾃定义类型成员变量，要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量，没有默认构造函数，那么就会报错。
//	n1.Print();
//
//	Date n2(2025, 4, 26);
//	n2.Print();
//
//	////n1和n2成功运行的话要么有构成函数重载的 1.⽆参构造函数 和 2.带参的构造函数 注释掉3，要么只有3.全缺省构造函数 注释掉1. 2.
//
//	//Date d2(2025);
//	//d2.Print();
//
//	//Date d3(2025, 4);
//	//d3.Print();
//
//	//Date d4(2025, 4, 26);
//	//d4.Print();
//
//	//Data n3();//怕你是函数定义，注意语法错误
//	// warning C4930: “Data n3(void)”: 未调用原型函数(是否是有意用变量定义的?)
//
//	return 0;
//}

typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		cout << "Stack(int n = 4)" << endl;
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_capacity = _top = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

// 两个栈实现一个队列
class Myqueue
{
private:
	// 自定义类型
	Stack _pushst;
	Stack _popst;
};

//int main()
//{
//	Myqueue q;//编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造，完成了两个成员的初始化
//
//	Stack st;
//	//destroy是倒着来释放  动态申请的内存，所以先释放st，再释放q。
//	return 0;
//}

//------------------------------------------------------------------------
//析构函数

//肯定会有析构函数，显式写就不默认生成，不显式写就会默认生成。但是我们不写编译器自动生成的析构函数对内置类型成员不做处理，
// 而对于自定义类型应该要自己写析构函数，因为这样才能清理好资源（不写编译器自动生成的析构函数调式没发现有清理）。也就是说自定义类型成员无论什么情况都会自动调用析构函数。

//大部分类都需要自己显式写构造函数，少部分不用
//大部分类都不需要自己显式写析构函数，少部分例如stack要

//struct B
//{
//	int* a;
//	int top;
//	int capacity;
//};
//int main()
//{
//	printf("%zd", sizeof(struct B));
//	return 0;
//}